/*
 * Open Source Physics software is free software as described near the bottom of this code file.
 *
 * For additional information and documentation on Open Source Physics please see:
 * <http://www.opensourcephysics.org/>
 */

package org.opensourcephysics.numerics.specialfunctions;
import org.opensourcephysics.numerics.Function;

/**
 * Computes the error function for a real argument.
 *
 * Can be instantiated as a Function or can be used with static methods.
 *
 */
public class ErrorFunction implements Function {
  /**
   * Evaluates the error function in order to implement the Function interface.
   *
   * @param x
   * @return error function at x
   */
  public double evaluate(double x) {
    return errf(x);
  }

  /**
   * Error function.
   * @param x
   * @return value of error function at x
   */
  public static double errf(double x) {
    if(x>26.0) {
      return 1.0;
    } else if(x<-5.5) {
      return -1.0;
    } else {
      double absx, c, p, q;
      absx = Math.abs(x);
      if(absx<=0.5) {
        c = x*x;
        p = ((-0.356098437018154e-1*c+0.699638348861914e1)*c+0.219792616182942e2)*c+0.242667955230532e3;
        q = ((c+0.150827976304078e2)*c+0.911649054045149e2)*c+0.215058875869861e3;
        return x*p/q;
      }
      if(x<0.0) {
        return -(1.0-Math.exp(-x*x)*nonexperfc(absx));
      }
      return 1.0-Math.exp(-x*x)*nonexperfc(absx);
    }
  }

  private static double nonexperfc(double x) {
    double absx, c, p, q;
    absx = Math.abs(x);
    if(absx<=0.5) {
      return Math.exp(x*x)*errf(x);
    } else if(absx<4.0) {
      c = absx;
      p = ((((((-0.136864857382717e-6*c+0.564195517478974e0)*c+0.721175825088309e1)*c+0.431622272220567e2)*c+0.152989285046940e3)*c+0.339320816734344e3)*c+0.451918953711873e3)*c+0.300459261020162e3;
      q = ((((((c+0.127827273196294e2)*c+0.770001529352295e2)*c+0.277585444743988e3)*c+0.638980264465631e3)*c+0.931354094850610e3)*c+0.790950925327898e3)*c+0.300459260956983e3;
      return((x>0.0) ? p/q : Math.exp(x*x)*2.0-p/q);
    } else {
      c = 1.0/x/x;
      p = (((0.223192459734185e-1*c+0.278661308609648e0)*c+0.226956593539687e0)*c+0.494730910623251e-1)*c+0.299610707703542e-2;
      q = (((c+0.198733201817135e1)*c+0.105167510706793e1)*c+0.191308926107830e0)*c+0.106209230528468e-1;
      c = (c*(-p)/q+0.564189583547756)/absx;
      return((x>0.0) ? c : Math.exp(x*x)*2.0-c);
    }
  }

}

/*
 * Open Source Physics software is free software; you can redistribute
 * it and/or modify it under the terms of the GNU General Public License (GPL) as
 * published by the Free Software Foundation; either version 2 of the License,
 * or(at your option) any later version.

 * Code that uses any portion of the code in the org.opensourcephysics package
 * or any subpackage (subdirectory) of this package must must also be be released
 * under the GNU GPL license.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
 * or view the license online at http://www.gnu.org/copyleft/gpl.html
 *
 * Copyright (c) 2007  The Open Source Physics project
 *                     http://www.opensourcephysics.org
 */
